'''
Created on 30/07/2009

@author: Tony Allan
'''

# Imports
#
import unittest
import sys
import types

from datastore.resource import *
from datastore.uri import *


RESOURCE_HEADER = """<?xml version='1.0' encoding='UTF-8'?>
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" """ + \
"""xsi:schemaLocation="http://yarra.googlecode.com/files/resource-1.0.xsd">"""

EMPTY_RESOURCE = RESOURCE_HEADER + \
"""</resource>"""

TEST_RESOURCE = """<?xml version='1.0' encoding='UTF-8'?>
<resource
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://yarra.googlecode.com/files/resource-1.0.xsd">

    <metadata>
        <item key="datastore:resource/type" type="yarra:type/uri">package:type/manifest</item>
        <item key="dc:title" type="yarra:type/string">My first package</item>
        <item key="dc:description" type="yarra:type/string">My package has lots of useful functionality.</item>
        <item key="yarra:version" type="yarra:type/string">0.1</item>
    </metadata>

    <content>
        <list key="package:manifest/modules">
            <item type="yarra:type/string">module one</item>
            <item type="yarra:type/string">module two</item>
        </list>
        <list key="package:manifest/tasks">
            <item type="yarra:type/string">task one</item>
            <item type="yarra:type/string">task two</item>
        </list>
        <list key="package:manifest/files">
            <item type="yarra:type/string">file one</item>
            <item type="yarra:type/string">file two</item>
        </list>
        <item key="test_cdata" type="yarra:type/string"><![CDATA[<a>]]> before <![CDATA[a non-XML tag <hr>]]> after</item>
    </content>
</resource>
"""

RESOURCE_EVERY_TYPE = """<?xml version='1.0' encoding='UTF-8'?>
<resource id="001"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://yarra.googlecode.com/files/resource-1.0.xsd">
    <metadata>
        <item key="dc:title" type="yarra:type/string">My first package</item>
        <item key="datastore:resource/type" type="yarra:type/uri">package:type/manifest</item>
    </metadata>

    <content>
        <dict key="dict1" />
        <dict key="dict2">
            <item key="z" type="yarra:type/string">z value</item>
        </dict>
        <list key="list1" />
        <list key="list2">
            <item type="yarra:type/int">1</item>
            <item type="yarra:type/int">2</item>
            <item type="yarra:type/string">three</item>
        </list>
        <item key="type_boolean_t" type="yarra:type/boolean">True</item>
        <item key="type_boolean_f" type="yarra:type/boolean">False</item>
        <item key="type_float" type="yarra:type/float">3.1415926</item>
        <item key="type_int" type="yarra:type/int">123</item>
        <item key="yarra:version" type="yarra:type/int">456</item>
        <item key="type_long" type="yarra:type/long">100200300400500600</item>
        <item key="type_none" type="yarra:type/none" />
        <item id="100" key="type_resource" type="yarra:type/resource" />
        <item key="type_string" type="yarra:type/string">a string.</item>
        <item key="empty_string" type="yarra:type/string"></item>
        <item key="type_uri" type="yarra:type/uri">yarra:version</item>
        <item key="type_base64" type="yarra:type/base64">c29tZSBiYXNlNjQgdGV4dC4=</item>
        <tuple key="type_tuple1" />
        <tuple key="type_tuple2">
            <item type="yarra:type/int">1</item>
            <item type="yarra:type/int">2</item>
            <item type="yarra:type/string">three</item>
        </tuple>
        <item key="type_unicode" type="yarra:type/unicode">a unicode string.</item>
    </content>
</resource>
"""


class ResourceTest(unittest.TestCase):
                
    def setUp(self):
        # The namespace is hardcoded here and in Resource()
        self.ns = {
            Resource.BINDING_NS_NAME_YARRA : Resource.BINDING_NS_URI_YARRA,
            Resource.BINDING_NS_NAME_DATASTORE : Resource.BINDING_NS_URI_DATASTORE,
            Resource.BINDING_NS_NAME_PACKAGE : Resource.BINDING_NS_URI_PACKAGE,
            Resource.BINDING_NS_NAME_DUBLINCORE : Resource.BINDING_NS_URI_DUBLINCORE
            }
        self.uri = Uri(namespaces=self.ns)

    def tearDown(self):
        pass

    # ---------- Serialise tests ----------
    
    def test_serialise_case_01(self):
        """[resource] serialise a valid but empty resource"""
        res_text = RESOURCE_HEADER + """<content /></resource>"""
        self.assertEqual(res_text, Resource(),
            "Resources not equal: \n%s\n%s\n" % (res_text, Resource()))

    def test_serialise_case_02(self):
        """[resource] serialise each supported type"""
        res = Resource()
        dc = Uri('dc:', self.ns)
        yarra = Uri('yarra:', self.ns)
        package = Uri('package:', self.ns)
        #FIXME: these shouldn't need to be converted to string first
        res.set_type(package.type.manifest)
        res.set_metadata(dc.title, 'My first package')
        res.id = "001"
        res.type_none = None
        res.type_boolean_t = True
        res.type_boolean_f = False
        res.set_var(yarra.version, 456)
        res.type_int = 123
        res.type_long = 100200300400500600
        res.type_float = 3.1415926
        res.type_unicode = u'a unicode string.'
        res.type_tuple1 = ()
        res.type_tuple2 = (1, 2, 'three')
        res.type_string = 'a string.'
        res.empty_string = ""
        res.type_uri = yarra.version
        res.type_base64 = Data("some base64 text.")
        another_res = Resource()
        another_res.id = 100
        res.type_resource = another_res
        res.list1 = []
        res.list2 = [1, 2, 'three']
        res.dict1 = dict()
        res.dict2 = {'z': "z value"}
        # this resource contains a resource reference
        res_et = Resource()
        pool = dict()
        res_et.deserialise(RESOURCE_EVERY_TYPE, pool)
        self.assertEqual(res.serialise(), res_et,
            "Resources not equal: \n%s\n%s\n" % (res.serialise(), res_et))

    def test_serialise_case_03(self):
        """[resource] serialise and deserialise the same resource"""
        self.assertEqual(Resource(TEST_RESOURCE), 
            Resource(str(Resource(str(Resource(str(Resource(TEST_RESOURCE))))))),
            "Resources not equal: \n%s\n%s\n" % 
                (Resource(TEST_RESOURCE), Resource(TEST_RESOURCE)))

    def test_serialise_case_04(self):
        """[resource] serialise - ignore class private variables"""
        class Z(Resource):
            pass
        res1 = Z()
        res1.__priv = "a private variable"
        res2 = Resource()
        self.assertEqual(res1, res2,
            "Resources not equal: \n%s\n%s\n" % (res1, res2))

    def test_serialise_case_05(self):
        """[resource] serialise - serialise_content()"""
        res1 = Resource()
        res1.a = "a var"
        res1.b = 123
        res1.set_metadata(Uri('m'), "meta")
        res1.id = "001"
        self.assertEqual(res1.a, "a var")
        self.assertEqual(res1.b, 123)
        self.assertEqual(res1.id, u"001")
        self.assertEqual(res1.get_single_metadata(Uri('m')), "meta")
        res2 = Resource(res1.serialise_content())
        self.assertEqual(res2.a, "a var")
        self.assertEqual(res2.b, 123)
        self.assertEqual(res2.id, None)
        self.assertEqual(res2.metadata_has_key(Uri('m')), False)

    def test_serialise_case_06(self):
        """[resource] serialise an unknown type"""
        class Z(object):
            pass
        res1 = Resource()
        res1.z = Z()
        print("\nExpecting output: " + \
            "'Warning: unknown type for item z. Setting to string.'")
        res2 = Resource(res1.serialise())
        self.assertEqual(type(res2.z), types.StringType,
            "Resource item type is not string: \n%s" % res2)

    def test_serialise_case_07(self):
        """[resource] serialise an unknown type with strict checking"""
        class Z(object):
            pass
        res1 = Resource(strict=True)
        res1.z = Z()
        try:
            res2 = Resource(res1.serialise())
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Unknown type for item: z.")

    def test_serialise_case_08(self):
        """[resource] serialise - deserialise - verify reserved XML characters are handled correctly"""
        res1 = Resource()
        res1.content = "<html>some html & more html</html>"
        #res1.b = 123
        res1.set_metadata(Uri('m'), "meta")
        res1.id = "001"
        res2 = Resource(res1.serialise())
        self.assertEqual(res1.content, "<html>some html & more html</html>")

    # ---------- Deserialise positive tests ----------
    
    def test_deserialise_case_01(self):
        """[resource] deserialise and check values"""
        # this resource contains a resource reference so use a pool
        res = Resource(namespaces=self.ns)
        yarra = Uri('yarra:', self.ns)
        pool = dict()
        res.deserialise(RESOURCE_EVERY_TYPE, pool)
        assert(res.type_none == None)
        assert(res.type_boolean_t == True)
        assert(res.type_boolean_f == False)
        assert(res.type_int == 123)
        assert(res.type_long == 100200300400500600)
        assert(res.type_float == 3.1415926)
        assert(res.type_unicode == u'a unicode string.')
        assert(res.type_tuple1 == ())
        assert(res.type_tuple2 == (1, 2, 'three'))
        assert(res.type_string == 'a string.')
        assert(res.empty_string == '')
        assert(res.type_resource.id == "100")
        assert(res.list1 == [])
        assert(res.list2 == [1, 2, 'three'])
        assert(res.dict1 == dict())
        assert(res.dict2 == {'z': "z value"})
        # test retrieving metadata by full and shorten uri
        self.assertEqual(res.get_single_metadata(Uri('http://yarra-project.com/terms/base/datastore/resource/type')), 
                                                 Uri('package:type/manifest', self.ns))
        self.assertEqual(res.get_single_metadata(Uri('datastore:resource/type', self.ns)), 
                                                 Uri('package:type/manifest', self.ns))
        # the key is always stored as a string
        assert(res.get_var(str(yarra.version)) == 456)
    
    def test_deserialise_case_02(self):
        """[resource] deserialise CDATA"""
        res = Resource(namespaces=self.ns)
        yarra = Uri('yarra:', self.ns)
        res.deserialise(TEST_RESOURCE)
        test = "<a> before a non-XML tag <hr> after"
        assert(res.test_cdata == test)

    # ---------- Deserialise negative tests ----------
    
    def test_deserialise_negative_case_01(self):
        """[resource] deserialise from an invalid type"""
        td = "<zzz />"
        try:
            Resource(999)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Expected string or buffer.")

    def test_deserialise_negative_case_02(self):
        """[resource] deserialise - wrong root node (not resource)"""
        td = "<zzz />"
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing 'resource' root element.")

    def test_deserialise_negative_case_03(self):
        """[resource] deserialise with missing item key"""
        td = RESOURCE_HEADER + """
            <content>
                <item />
            </content></resource>
            """
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing key for element: item.")

    def test_deserialise_negative_case_04(self):
        """[resource] deserialise with missing item type"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="%s/z" />
            </content></resource>
            """ % yarra.type
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing type for element: item.")

    def test_deserialise_negative_case_05(self):
        """[resource] deserialise with incorrect boolean value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z</item>
            </content></resource>
            """ % yarra.type.boolean
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid boolean value for element(item).")
 
    def test_deserialise_negative_case_06(self):
        """[resource] deserialise with incorrect int value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z</item>
            </content></resource>
            """ % yarra.type.int
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid int value for element(item).")

    def test_deserialise_negative_case_07(self):
        """[resource] deserialise with incorrect long value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z</item>
            </content></resource>
            """ % yarra.type.long
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid long value for element(item).")

    def test_deserialise_negative_case_08(self):
        """[resource] deserialise with incorrect float value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z</item>
            </content></resource>
            """ % yarra.type.float
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid float value for element(item).")

    def test_deserialise_negative_case_09(self):
        """[resource] deserialise with incorrect unicode value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">\x80abc</item>
            </content></resource>
            """ % yarra.type.unicode
        try:
            # The error is actually detected during the XML parse.
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Expat parse error: not well-formed (invalid token): line 4, column 56")

    def test_deserialise_negative_case_10(self):
        """[resource] deserialise with missing id in a reference"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % yarra.type.resource
        try:
            res = Resource()
            pool = dict()
            res.deserialise(td, pool)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing id for resource element(item).")

    def test_deserialise_negative_case_11(self):
        """[resource] deserialise with an unknown element"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <z key="z" type="%s" />
            </content></resource>
            """ % yarra.type.none
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Unknown element(z).")

    def test_deserialise_negative_case_12(self):
        """[resource] deserialise with an int converted to a long"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">100200300400500600700800900</item>
            </content></resource>
            """ % yarra.type.int
        res = Resource(td)
        # z is autmatically converted to a long by Python
        self.assertEqual(type(res.z), types.LongType)

    def test_deserialise_negative_case_13(self):
        """[resource] deserialise with an invalid int"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z100200300400500600700800900</item>
            </content></resource>
            """ % yarra.type.int
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid int value for element(item).")

    def test_deserialise_negative_case_14(self):
        """[resource] serialise with an unknown type"""
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="yarra:type/fred">abc</item>
            </content></resource>
            """
        Resource(td)
        assert True

    # cases for Boolean, Int, Long, Float, Unicode, Tuple, and String with no value.
 
    def test_deserialise_negative_case_none_boolean(self):
        """[resource] deserialise with a boolean with no value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % yarra.type.boolean
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % yarra.type.boolean)

    def test_deserialise_negative_case_none_int(self):
        """[resource] deserialise with a int with no value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % yarra.type.int
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % yarra.type.int)
 
    def test_deserialise_negative_case_none_long(self):
        """[resource] deserialise with a long with no value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % yarra.type.long
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % yarra.type.long)
 
    def test_deserialise_negative_case_none_float(self):
        """[resource] deserialise with a float with no value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % yarra.type.float
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % yarra.type.float)
 
    def test_deserialise_negative_case_none_unicode(self):
        """[resource] deserialise with a unicode with no value"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % yarra.type.unicode
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % yarra.type.unicode)

    def test_deserialise_negative_case_none_tuple(self):
        """[resource] deserialise with a tuple with no value"""
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="tuple" />
            </content></resource>
            """
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (tuple).")

    def test_deserialise_negative_incorrect_element(self):
        """[resource] deserialise with incorrect element under resource"""
        td = RESOURCE_HEADER + """
                <zzz />
            </resource>
            """
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Unknown element under resource: zzz.")

    # ---------- Comparison tests ----------

    def test_compare_case_01(self):
        """[resource] compare - equivalent resources"""
        res1 = Resource()
        res1.type_int = 123
        res1.type_string = 'a string.'
        res2 = Resource()
        res2.type_int = 123
        res2.type_string = 'a string.'
        self.assertEqual(res1, res2,
            "Resources not equal: \n%s\n%s\n" % (res1, res2))

    def test_compare_case_02(self):
        """[resource] compare - equivalent resources with the same id"""
        res1 = Resource()
        res1.id = 001
        res1.type_int = 123
        res1.type_string = 'a string.'
        res2 = Resource()
        res2.id = 001
        res2.type_int = 123
        res2.type_string = 'a string.'
        self.assertEqual(res1, res2,
            "Resources not equal: \n%s\n%s\n" % (res1, res2))

    def test_compare_case_03(self):
        """[resource] compare - equivalent resources with mismatched id's"""
        res1 = Resource()
        res1.id = 001
        res1.type_int = 123
        res1.type_string = 'a string.'
        res2 = Resource()
        res2.type_int = 123
        res2.type_string = 'a string.'
        self.assertNotEqual(res1, res2,
            "Resources incorrectly equal: \n%s\n%s\n" % (res1, res2))

    def test_compare_case_04(self):
        """[resource] compare - resource sort order"""
        res1 = Resource()
        res1.id = 001
        res1.type_int = 123
        res1.type_string = 'a string.'
        res2 = Resource()
        res2.id = 002
        res2.type_int = 123
        res2.type_string = 'a string.'
        if res1 < res2:
            assert True
        else:
            self.fail("comparison test failed.")

    def test_compare_case_05(self):
        """[resource] compare - different resources"""
        res1 = Resource()
        res1.id = 001
        res1.type_string = 'a string.'
        res2 = Resource()
        res2.id = 002
        res2.type_int = 123
        self.assertNotEqual(res1, res2,
            "Resources incorrectly equal: \n%s\n%s\n" % (res1, res2))

    def test_compare_case_06(self):
        """[resource] compare - resource and something else"""
        res1 = Resource()
        res1.id = 001
        res1.type_string = 'a string.'
        self.assertNotEqual(res1, dict(),
            "Resource incorrectly matches with dict(): \n%s" % res1)

    def test_compare_case_07(self):
        """[resource] compare - other is none"""
        res1 = Resource()
        res1.id = 001
        res1.type_string = 'a string.'
        self.assertNotEqual(res1, None,
            "Resource incorrectly matches with None: \n%s" % res1)

    # ---------- ID tests ----------

    def test_id_case_01(self):
        """[resource] id - set_id"""
        res1 = Resource()
        res1.set_id("001")
        self.assertEqual(res1.id, "001")

    def test_id_case_02(self):
        """[resource] id - get_id"""
        res1 = Resource()
        res1.set_id("001")
        self.assertEqual(res1.get_id(), "001")

    def test_id_case_03(self):
        """[resource] id - get_single_metadata exception"""
        res1 = Resource()
        try:
            res1.id = None
            assert False
        except TypeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Resource ID cannot be None.")

    # ---------- Metadata tests ----------

    def test_metadata_case_01(self):
        """[resource] metadata - get_single_metadata"""
        res1 = Resource()
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('z'), "meta z")
        self.assertEqual(res1.get_single_metadata(Uri('y')), "meta y")
        self.assertEqual(res1.get_single_metadata(Uri('z')), "meta z")

    def test_metadata_case_02(self):
        """[resource] metadata - set multiple metadata with the same key"""
        res1 = Resource()
        res1.id = 001
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('y'), "meta y2")
        res1.set_metadata(Uri('z'), "meta z")
        self.assertEqual(len(res1.get_metadata(Uri('y'))), 3)
        self.assertEqual(res1.get_metadata(Uri('y')), 
                         ["meta y", "meta y", "meta y2"])
        self.assertEqual(res1.get_metadata(Uri('z')), 
                         ["meta z"])

    def test_metadata_case_03(self):
        """[resource] metadata - get_single_metadata exception"""
        res1 = Resource()
        res1.id = 001
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('y'), "meta y with another value")
        try:
            dummy = res1.get_single_metadata(Uri('y'))
            assert False
        except IndexError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Exactly one value expected for key: y")

    def test_metadata_case_04(self):
        """[resource] metadata - get_single_metadata exception"""
        res1 = Resource()
        res1.id = 001
        try:
            dummy = res1.get_single_metadata(Uri('y'))
            assert False
        except IndexError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Key not found: y")

    def test_metadata_case_05 (self):
        """[resource] metadata"""
        res1 = Resource()
        res1.set_metadata(Uri('m'), "meta m")
        res1.set_metadata(Uri('n'), "meta n")
        res1.id = "001"
        self.assertEqual(res1.get_single_metadata(Uri('m')), "meta m")
        self.assertEqual(res1.get_single_metadata(Uri('n')), "meta n")
        self.assertEqual(res1.get_all_metadata(), [
            [Uri('m'), "meta m"],
            [Uri('n'), "meta n"]
            ])
        res2 = Resource(res1.serialise())
        self.assertEqual(res2.get_single_metadata(Uri('m')), "meta m")
        self.assertEqual(res2.get_single_metadata(Uri('n')), "meta n")
        self.assertEqual(res2.metadata_has_key(Uri('m')), True)
        self.assertEqual(res2.metadata_has_key(Uri('dummy')), False)
        self.assertEqual(res2.get_all_metadata(), [
            [Uri('m'), "meta m"],
            [Uri('n'), "meta n"]
            ])

    def test_metadata_case_06 (self):
        """[resource] metadata - delete metadata"""
        res1 = Resource()
        res1.set_metadata(Uri('m'), "meta m")
        res1.set_metadata(Uri('n'), "meta n")
        res1.set_metadata(Uri('n'), "meta n")
        res1.id = "001"
        res2 = Resource(res1.serialise())
        
        self.assertEqual(res1.get_single_metadata(Uri('m')), "meta m")
        self.assertEqual(res1.get_metadata(Uri('n')), ["meta n", "meta n"])
        res1.delete_metadata(Uri('m'), "meta m")
        self.assertEqual(res1.metadata_has_key(Uri('m')), False)
        res1.delete_metadata(Uri('n'), "meta n")
        self.assertEqual(res1.metadata_has_key(Uri('n')), True)
        res1.delete_metadata(Uri('n'), "meta n")
        self.assertEqual(res1.metadata_has_key(Uri('n')), False)
        self.assertEqual(res1.get_all_metadata(), [])
        
        # and make sure that all is OK in a copy of the resource
        self.assertEqual(res2.get_single_metadata(Uri('m')), "meta m")
        self.assertEqual(res2.get_metadata(Uri('n')), ["meta n", "meta n"])
        res2.delete_metadata(Uri('m'), "meta m")
        self.assertEqual(res2.metadata_has_key(Uri('m')), False)
        res2.delete_metadata(Uri('n'), "meta n")
        self.assertEqual(res2.metadata_has_key(Uri('n')), True)
        res2.delete_metadata(Uri('n'), "meta n")
        self.assertEqual(res2.metadata_has_key(Uri('n')), False)
        self.assertEqual(res2.get_all_metadata(), [])

    def test_metadata_case_07 (self):
        """[resource] metadata - fetch non-existent metadata"""
        res1 = Resource()       
        self.assertEqual(res1.get_metadata(Uri('fred')), [])
        
    def test_metadata_case_08(self):
        """[resource] metadata with missing key"""
        yarra = Uri('yarra:', self.ns)
        td = RESOURCE_HEADER + """
            <metadata>
                <item type="%s">z</item>
            </metadata></resource>
            """ % yarra.type.string
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing key for metadata element.")

    def test_metadata_case_09(self):
        """[resource] metadata type error on _get_metadata_key"""
        res = Resource()
        try:
            res.metadata_has_key('not a uri')
            assert False
        except TypeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Key (not a uri) is not a Uri.")

    def test_metadata_case_10(self):
        """[resource] metadata type error on get"""
        res = Resource()
        try:
            res.get_metadata('not a uri')
            assert False
        except TypeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Key (not a uri) is not a Uri.")

    def test_metadata_case_11(self):
        """[resource] metadata delete 2"""
        yarra = Uri('yarra:', self.ns)
        res = Resource()
        res.set_metadata(yarra.test, 123)
        self.assertEqual(res.get_metadata(yarra.test), [123])
        try:
            res.delete_metadata(yarra.test, 123)
            assert True
        except:
            assert False

#    def test_metadata_case_12(self):
#        """[resource] metadata delete 3"""
#        yarra = Uri('yarra:', self.ns)
#        res = Resource()
#        try:
#            res.get_metadata(yarra.test)
#            assert True
#        except:
#            assert False

    def test_metadata_case_13(self):
        """[resource] metadata type error on delete"""
        yarra = Uri('yarra:', self.ns)
        res = Resource()
        res.set_metadata(yarra.test, 999)
        try:
            res.delete_metadata(yarra.test, 123)
            assert False
        except ValueError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Metadata key (http://yarra-project.com/terms/yarra/test) not found.")

 
    # ---------- Pool tests ----------

    def test_pool_case_01(self):
        """[resource] pool needed but not specified"""
        res1 = Resource()
        res1.a = "var a"
        res1.id = "001"
        res2 = Resource()
        res2.b = "var b"
        res2.id = "002"
        res2.res_number_one = res1
        res2_xml = res2.serialise()
        try:
            res2_new = Resource()
            res2_new.deserialise(res2_xml) # No pool specified
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Resource reference found but no pool specified.")

    def test_pool_case_02(self):
        """[resource] pool error - same resource deserialised twice"""
        res1 = Resource()
        res1.a = "var a"
        res1.id = "001"
        res2 = Resource()
        res2.b = "var b"
        res2.id = "002"
        res2.res_number_one = res1
        res2_xml = res2.serialise()
        try:
            pool = dict()
            res2_new = Resource()
            res2_new.deserialise(res2_xml, pool)
            res3_new = Resource()
            res3_new.deserialise(res2_xml, pool)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Resource 002 already exists in the pool.")
   
    def test_pool_case_03(self):
        """[resource] pool - deserialise resource with a resource reference"""
        res1 = Resource()
        res1.a = "var a"
        res1.id = "001"
        res1_xml = res1.serialise()
        res2 = Resource()
        res2.b = "var b"
        res2.id = "002"
        res2.res_number_one = res1
        res2_xml = res2.serialise()
        pool = dict()
        res1_new = Resource()
        res1_new.deserialise(res1_xml, pool)
        res2_new = Resource()
        res2_new.deserialise(res2_xml, pool)
        self.assertEqual(res2_xml, res2_new.serialise(),
            "Resources should be equal: \n%s\n%s\n" % (res2_xml, res2_new.serialise()))
        self.assertEqual(res2_new.b, "var b")
                    
    def test_pool_case_04(self):
        """[resource] pool with reversed deserialisation"""
        res1 = Resource()
        res1.a = "var a"
        res1.id = "001"
        res1_xml = res1.serialise()
        res2 = Resource()
        res2.b = "var b"
        res2.id = "002"
        res2.res_number_one = res1
        res2_xml = res2.serialise()
        pool = dict()
        # res2 is deserialised before res1
        res2_new = Resource()
        res2_new.deserialise(res2_xml, pool)
        res1_new = Resource()
        res1_new.deserialise(res1_xml, pool)
        self.assertEqual(res2_xml, res2_new.serialise(),
            "Resources should be equal: \n%s\n%s\n" % (res2_xml, res2_new.serialise()))
        self.assertEqual(res2_new.b, "var b")

    # ---------- Other tests ----------

    def test_other_comment_case_01(self):
        """[resource] other - test text mark-up and comments"""
        td = RESOURCE_HEADER + """
            <!-- comment -->
            <metadata>
                <!-- comment -->
                <item key="dc:description" type="yarra:type/string">My package has lots of useful functionality.</item>
                <!-- comment -->
                <item key="dc:title" type="yarra:type/string">My first package</item>
                <!-- comment -->
                <item key="yarra:version" type="yarra:type/string">0.1</item>
                <!-- comment -->
            </metadata>
            <!-- comment -->
            <content>
                <!-- comment -->
                <list key="aaa">
                    <!-- comment -->
                    <item type="yarra:type/string">module <br /> one</item>
                    <!-- comment -->
                    <item type="yarra:type/string">module <!-- comment --> two</item>
                </list>
                <!-- comment -->
                <list key="bbb">
                    <item type="yarra:type/string">task one</item>
                    <!-- comment -->
                    <item type="yarra:type/string">task two</item>
                </list>
                <!-- comment -->
                <tuple key="ccc">
                    <item type="yarra:type/string">task one</item>
                    <!-- comment -->
                    <item type="yarra:type/string">task two</item>
                </tuple>
                <!-- comment -->
            </content><!-- comment --></resource>
            """
        res = Resource(td)
        test = "module  one"
        self.assertEqual(res.aaa[0], test,
            "Strings should be equal: \n%s\n%s\n" % (res.aaa[0], test))

    def test_other_get_metadata_keys(self):
        """[resource] other get_metadata_keys"""
        res = Resource(TEST_RESOURCE)
        yarra = Uri('yarra:', self.ns)
        datastore = Uri('datastore:', self.ns)
        dc = Uri('dc:', self.ns)
        keys = res.get_metadata_keys()
        expected_keys = [datastore.resource.type,
                         dc.description,
                         dc.title, 
                         yarra.version
                         ]
        for expected_key in expected_keys:
            self.assertTrue(expected_key in keys, "Resources not equal: expected_key %s not found" % (expected_key))

    def test_other_get_resource_type(self):
        """[resource] other get_resource_type"""
        res = Resource(TEST_RESOURCE)
        package = Uri('package:', self.ns)
        type = res.get_type()
        self.assertEqual(type, package.type.manifest,
            "Resources not equal: \n%s\n%s\n" % 
                (type, package.type.manifest))
        res.set_type('abc')
        self.assertEqual(res.get_type(), 'abc',
            "Resources not equal: \n%s\n%s\n" % 
                (res.get_type(), 'abc'))

    def test_other_load_from_file(self):
        """[resource] other load_from_file"""
        res = Resource()
        #import os
        #print('Directory:', os.listdir("."))
        #res.load_from_file('./configuration.xml')
        try:
            res.load_from_file('./configuration.xml')
        except:
            res.load_from_file('datastore/configuration.xml')
        res_type = res.get_type()
        self.assertEqual(res_type, Uri('package:type/configuration', self.ns),
            "Resource types not equal: \n%s\n%s\n" % 
                (res_type, Uri('package:type/configuration')))
        cont = res.get_var('package:manifest/spaces')['dc:contributor']['package:label']
        self.assertEqual(cont, 'DC - Contributor',
            "Resource contributers not equal: \n%s\n%s\n" % 
                (cont, 'DC - Contributor'))

    def test_pretty_print(self):
        """[resource] other pretty_print"""
        res = Resource()
        res.set_id(1)
        res.set_metadata(Uri("dc:title"), "value")
        self.assertEqual(res.pretty_print(), 
"""<resource {http://www.w3.org/2001/XMLSchema-instance}schemaLocation="http://yarra.googlecode.com/files/resource-1.0.xsd" id="1" >
  <metadata>
    <item type="yarra:type/string" key="dc:title" >
      value
    </item>
  </metadata>
  <content>
  </content>
</resource>
""")
        
    def test_var_methods(self):
        """[resource] other get_vars, get_var, set_var, del_var"""
        res = Resource()
        res.a = 10
        res.b = 'aaa'
        res.set_var('1#2', 123)
        self.assertEqual(res.a, 10)
        self.assertEqual(res.get_var('a'), 10)
        self.assertEqual(res.b, 'aaa')
        self.assertEqual(res.get_var('1#2'), 123)
        self.assertTrue(res.has_var('1#2'))
        res.del_var('a')
        try:
            print(res.a)
            assert False
        except AttributeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "'Resource' object has no attribute 'a'")

    def test_get_var_part(self):
        """[resource] other get_var_part"""
        res = Resource()
        res.a = "qaaa"
        res.b = [['a1', ['qaa1', 'qaa2']], ['b1', ['qbb1', 'qbb2']], ['c1', ['qcc1', 'qcc2']]]
        res.c = "qccc"
        res.d = "qddd"
        res.x = [{'a1':'q11','a2':'q12'}, {'a1':'q21','a2':'q22'}, {'a1':'q31','a2':'q32'}]
        y = {0:'q50',1:'q51'}
        res.y = y
        z = {'0':'q60','1':'q61'}
        res.z = z
        self.assertEqual(res.get_var_part(None), res.get_vars())
#        self.assertEqual(res.get_var_part(''), res.get_vars())
        self.assertEqual(res.get_var_part('a'), "qaaa")
        self.assertEqual(res.get_var_part('b.1.1.1'), "qbb2")
        self.assertEqual(res.get_var_part('x.1.a2'), "q22")
        self.assertEqual(res.get_var_part('y'), y)
        self.assertEqual(res.get_var_part('y.1'), 'q51')
        self.assertEqual(res.get_var_part('z.1'), 'q61')
        # test getting an empty part and a part with a null fragment specified.
        self.assertEqual(res.get_var_part(''), res.get_vars())
        # and a part of None
        self.assertEqual(res.get_var_part(None), res.get_vars())
        try:
            res.get_var_part(0)
            assert False
        except GetPartException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Part '0' is not a string.")
        try:
            res.get_var_part(3.1415926)
            assert False
        except GetPartException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Part '3.1415926' is not a string.")
        try:
            res.get_var_part('b.a2')
            assert False
        except GetPartException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Unable to get 'b.a2' for the Resource.")

    def test_dict_attribute_01(self):
        """[resource] attribute access to dict values"""
        res1 = Resource()
        res1.dict = ResourceDict()
        res1.dict.aaa = 123
        res1.dict.bbb = ResourceDict()
        res1.dict.bbb.ccc = 456
        res1.id = "001"
        res1_xml = res1.serialise()
        res2 = Resource(res1_xml)
        self.assertEqual(res1.dict.aaa, 123)
        self.assertEqual(res1.dict.bbb.ccc, 456)
        self.assertEqual(res2.dict.aaa, 123)
        self.assertEqual(res2.dict.bbb.ccc, 456)
        self.assertEqual(res1.dict['aaa'], 123)
        self.assertEqual(res1.dict['bbb']['ccc'], 456)

#    def test_install_distribution_case_1(self):
#        '''[resource] compare two equal resources'''  
#        
#        PACKAGE_URI = 'http://yarra-project.com/terms/base/package/'
#        PACKAGE_FILENAME = PACKAGE_URI + 'filename'
#        PACKAGE_PACKAGENAME = PACKAGE_URI + 'packagename'
#       
#        # create test data
#        zip_file_name = 'test.yarra'
#        module1_name = 'moduleone'
#        module2_name = 'moduletwo'
#        module1_file_name = module1_name + '.py'
#        module2_file_name = module2_name + '.py'
#        module1_contents = """class TestClassA(object):
#    test_attrib_1 = 2"""
#        module2_contents = """class TestClassB(object):
#    test_attrib_1 = 'blah'"""
#        package_name = "testpackage"
#        distribution_name = "test_distribution"
#        distribution_file_name = distribution_name + ".xml"
#        distribution_xml = '''<?xml version="1.0" encoding="UTF-8"?>
#<distribution xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://yarra-project.com/element/ distribution.xsd">
#    <name>default</name>
#    <package>
#        <name>{0}</name>
#        <location>{1}</location>            
#    </package>
#</distribution>'''.format(package_name, 'file:///{0}/{1}'.format(os.getcwd(), zip_file_name))
#        configuration_file_name = 'configuration.xml'
#        configuration_content = '''<?xml version="1.0" encoding="UTF-8" ?>
#        <resource>
#            <content>
#                <list key="package:manifest/modules">
#                    <dict>
#                        <item key="package:filename" type="yarra:type/string">{0}</item>
#                        <item key="package:startup" type="yarra:type/boolean">True</item>
#                    </dict>
#                    <dict>
#                        <item key="package:filename" type="yarra:type/string">{1}</item>
#                        <item key="package:startup" type="yarra:type/boolean">True</item>
#                    </dict>                             
#                </list>
#            </content>
#        </resource>'''.format(module1_file_name, module2_file_name)
#        rs_1 = Resource(serialised_object=configuration_content)
#        rs_1.set_metadata(Uri(PACKAGE_PACKAGENAME), package_name)
#        rs_2 = Resource()
#        rs_2.content = base64.encodestring(module1_contents)
#        rs_2.set_metadata(Uri(PACKAGE_PACKAGENAME), package_name)        
#        rs_2.set_metadata(Uri('package:modulename'), ".".join([package_name, module1_name]))
#        rs_3 = Resource()
#        rs_3.content = base64.encodestring(module2_contents)
#        rs_3.set_metadata(Uri(PACKAGE_PACKAGENAME), package_name)        
#        rs_3.set_metadata(Uri('package:modulename'), ".".join([package_name, module2_name]))
#       
#        # change test data to match data that is stored (i.e. non encoded content and ids set)        
#        rs_2.set_id(2)
#        rs_2.content = module1_contents
#        rs_3.set_id(3)
#        rs_3.content = module2_contents
#        
#        if rs_2 == rs_3:
#            assert True              
#        else:
#            self.fail('failed')




if __name__ == "__main__":
    unittest.main()